{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#Habitat-sim Asset Viewer\n",
    "\n",
    "This utility provides a user with the ability to view assets rendered by the Habitat-Sim engine.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "lines_to_next_cell": 2
   },
   "outputs": [],
   "source": [
    "# @title Path Setup and Imports { display-mode: \"form\" }\n",
    "# @markdown (double click to show code).\n",
    "\n",
    "## [setup]\n",
    "import math\n",
    "import os\n",
    "\n",
    "import git\n",
    "import magnum as mn\n",
    "import numpy as np\n",
    "\n",
    "import habitat_sim\n",
    "from habitat_sim.bindings import built_with_bullet\n",
    "from habitat_sim.utils import common as ut\n",
    "from habitat_sim.utils import viz_utils as vut\n",
    "\n",
    "try:\n",
    "    import ipywidgets as widgets\n",
    "    from IPython.display import display as ipydisplay\n",
    "\n",
    "    # For using jupyter/ipywidget IO components\n",
    "\n",
    "    HAS_WIDGETS = True\n",
    "except ImportError:\n",
    "    HAS_WIDGETS = False\n",
    "\n",
    "repo = git.Repo(\".\", search_parent_directories=True)\n",
    "dir_path = repo.working_tree_dir\n",
    "data_path = os.path.join(dir_path, \"data\")\n",
    "# fmt: off\n",
    "output_directory = \"examples/tutorials/asset_viewer_output/\"  # @param {type:\"string\"}\n",
    "# fmt: on\n",
    "output_path = os.path.join(dir_path, output_directory)\n",
    "os.makedirs(output_path, exist_ok=True)\n",
    "\n",
    "# define some globals the first time we run.\n",
    "if \"sim\" not in globals():\n",
    "    global sim\n",
    "    sim = None\n",
    "    global obj_attr_mgr\n",
    "    obj_attr_mgr = None\n",
    "    global prim_attr_mgr\n",
    "    obj_attr_mgr = None\n",
    "    global stage_attr_mgr\n",
    "    stage_attr_mgr = None\n",
    "    global rigid_obj_mgr\n",
    "    rigid_obj_mgr = None"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "lines_to_next_cell": 2
   },
   "outputs": [],
   "source": [
    "# @title Define Configuration Utility Functions { display-mode: \"form\" }\n",
    "# @markdown (double click to show code)\n",
    "\n",
    "# @markdown This cell defines a number of utility functions used throughout the tutorial to make simulator reconstruction easy:\n",
    "# @markdown - make_cfg\n",
    "# @markdown - make_default_settings\n",
    "# @markdown - make_simulator_from_settings\n",
    "\n",
    "\n",
    "def make_cfg(settings):\n",
    "    sim_cfg = habitat_sim.SimulatorConfiguration()\n",
    "    sim_cfg.gpu_device_id = 0\n",
    "    sim_cfg.scene_id = settings[\"scene\"]\n",
    "    sim_cfg.enable_physics = settings[\"enable_physics\"]\n",
    "    # Optional; Specify the location of an existing scene dataset configuration\n",
    "    # that describes the locations and configurations of all the assets to be used\n",
    "    if \"scene_dataset_config\" in settings:\n",
    "        sim_cfg.scene_dataset_config_file = settings[\"scene_dataset_config\"]\n",
    "    if \"override_scene_light_defaults\" in settings:\n",
    "        sim_cfg.override_scene_light_defaults = settings[\n",
    "            \"override_scene_light_defaults\"\n",
    "        ]\n",
    "    if \"scene_light_setup\" in settings:\n",
    "        sim_cfg.scene_light_setup = settings[\"scene_light_setup\"]\n",
    "\n",
    "    # Note: all sensors must have the same resolution\n",
    "    sensor_specs = []\n",
    "    if settings[\"color_sensor_1st_person\"]:\n",
    "        color_sensor_1st_person_spec = habitat_sim.CameraSensorSpec()\n",
    "        color_sensor_1st_person_spec.uuid = \"color_sensor_1st_person\"\n",
    "        color_sensor_1st_person_spec.sensor_type = habitat_sim.SensorType.COLOR\n",
    "        color_sensor_1st_person_spec.resolution = [\n",
    "            settings[\"height\"],\n",
    "            settings[\"width\"],\n",
    "        ]\n",
    "        color_sensor_1st_person_spec.position = [0.0, settings[\"sensor_height\"], 0.0]\n",
    "        color_sensor_1st_person_spec.orientation = [\n",
    "            settings[\"sensor_pitch\"],\n",
    "            0.0,\n",
    "            0.0,\n",
    "        ]\n",
    "        color_sensor_1st_person_spec.sensor_subtype = habitat_sim.SensorSubType.PINHOLE\n",
    "        sensor_specs.append(color_sensor_1st_person_spec)\n",
    "    if settings[\"depth_sensor_1st_person\"]:\n",
    "        depth_sensor_1st_person_spec = habitat_sim.CameraSensorSpec()\n",
    "        depth_sensor_1st_person_spec.uuid = \"depth_sensor_1st_person\"\n",
    "        depth_sensor_1st_person_spec.sensor_type = habitat_sim.SensorType.DEPTH\n",
    "        depth_sensor_1st_person_spec.resolution = [\n",
    "            settings[\"height\"],\n",
    "            settings[\"width\"],\n",
    "        ]\n",
    "        depth_sensor_1st_person_spec.position = [0.0, settings[\"sensor_height\"], 0.0]\n",
    "        depth_sensor_1st_person_spec.orientation = [\n",
    "            settings[\"sensor_pitch\"],\n",
    "            0.0,\n",
    "            0.0,\n",
    "        ]\n",
    "        depth_sensor_1st_person_spec.sensor_subtype = habitat_sim.SensorSubType.PINHOLE\n",
    "        sensor_specs.append(depth_sensor_1st_person_spec)\n",
    "    if settings[\"semantic_sensor_1st_person\"]:\n",
    "        semantic_sensor_1st_person_spec = habitat_sim.CameraSensorSpec()\n",
    "        semantic_sensor_1st_person_spec.uuid = \"semantic_sensor_1st_person\"\n",
    "        semantic_sensor_1st_person_spec.sensor_type = habitat_sim.SensorType.SEMANTIC\n",
    "        semantic_sensor_1st_person_spec.resolution = [\n",
    "            settings[\"height\"],\n",
    "            settings[\"width\"],\n",
    "        ]\n",
    "        semantic_sensor_1st_person_spec.position = [\n",
    "            0.0,\n",
    "            settings[\"sensor_height\"],\n",
    "            0.0,\n",
    "        ]\n",
    "        semantic_sensor_1st_person_spec.orientation = [\n",
    "            settings[\"sensor_pitch\"],\n",
    "            0.0,\n",
    "            0.0,\n",
    "        ]\n",
    "        semantic_sensor_1st_person_spec.sensor_subtype = (\n",
    "            habitat_sim.SensorSubType.PINHOLE\n",
    "        )\n",
    "        sensor_specs.append(semantic_sensor_1st_person_spec)\n",
    "    if settings[\"color_sensor_3rd_person\"]:\n",
    "        color_sensor_3rd_person_spec = habitat_sim.CameraSensorSpec()\n",
    "        color_sensor_3rd_person_spec.uuid = \"color_sensor_3rd_person\"\n",
    "        color_sensor_3rd_person_spec.sensor_type = habitat_sim.SensorType.COLOR\n",
    "        color_sensor_3rd_person_spec.resolution = [\n",
    "            settings[\"height\"],\n",
    "            settings[\"width\"],\n",
    "        ]\n",
    "        color_sensor_3rd_person_spec.position = [\n",
    "            0.0,\n",
    "            settings[\"sensor_height\"] + 0.2,\n",
    "            0.2,\n",
    "        ]\n",
    "        color_sensor_3rd_person_spec.orientation = [-math.pi / 4, 0.0, 0.0]\n",
    "        color_sensor_3rd_person_spec.sensor_subtype = habitat_sim.SensorSubType.PINHOLE\n",
    "        sensor_specs.append(color_sensor_3rd_person_spec)\n",
    "\n",
    "    # Here you can specify the amount of displacement in a forward action and the turn angle\n",
    "    agent_cfg = habitat_sim.agent.AgentConfiguration()\n",
    "    agent_cfg.sensor_specifications = sensor_specs\n",
    "\n",
    "    return habitat_sim.Configuration(sim_cfg, [agent_cfg])\n",
    "\n",
    "\n",
    "def make_default_settings():\n",
    "    settings = {\n",
    "        \"width\": 1280,  # Spatial resolution of the observations\n",
    "        \"height\": 720,\n",
    "        \"scene\": os.path.join(\n",
    "            data_path, \"scene_datasets/mp3d_example/17DRP5sb8fy/17DRP5sb8fy.glb\"\n",
    "        ),  # Scene path\n",
    "        \"default_agent\": 0,\n",
    "        \"sensor_height\": 1.5,  # Height of sensors in meters\n",
    "        \"sensor_pitch\": -math.pi / 8.0,  # sensor pitch (x rotation in rads)\n",
    "        \"color_sensor_1st_person\": True,  # RGB sensor\n",
    "        \"color_sensor_3rd_person\": False,  # RGB sensor 3rd person\n",
    "        \"depth_sensor_1st_person\": False,  # Depth sensor\n",
    "        \"semantic_sensor_1st_person\": False,  # Semantic sensor\n",
    "        \"seed\": 1,\n",
    "        \"enable_physics\": built_with_bullet,  # enable dynamics simulation if bullet is present\n",
    "    }\n",
    "    return settings\n",
    "\n",
    "\n",
    "def make_simulator_from_settings(sim_settings):\n",
    "    cfg = make_cfg(sim_settings)\n",
    "    # clean-up the current simulator instance if it exists\n",
    "    global sim\n",
    "    global obj_attr_mgr\n",
    "    global prim_attr_mgr\n",
    "    global stage_attr_mgr\n",
    "    global rigid_obj_mgr\n",
    "    global metadata_mediator\n",
    "\n",
    "    if sim != None:\n",
    "        sim.close()\n",
    "    # initialize the simulator\n",
    "    sim = habitat_sim.Simulator(cfg)\n",
    "    # Managers of various Attributes templates\n",
    "    obj_attr_mgr = sim.get_object_template_manager()\n",
    "    obj_attr_mgr.load_configs(str(os.path.join(data_path, \"objects/example_objects\")))\n",
    "    prim_attr_mgr = sim.get_asset_template_manager()\n",
    "    stage_attr_mgr = sim.get_stage_template_manager()\n",
    "    # Manager providing access to rigid objects\n",
    "    rigid_obj_mgr = sim.get_rigid_object_manager()\n",
    "    # get metadata_mediator\n",
    "    metadata_mediator = sim.metadata_mediator\n",
    "\n",
    "    # UI-populated handles used in various cells.  Need to initialize to valid\n",
    "    # value in case IPyWidgets are not available.\n",
    "    # Holds the user's desired file-based object template handle\n",
    "    global sel_file_obj_handle\n",
    "    sel_file_obj_handle = obj_attr_mgr.get_file_template_handles()[0]\n",
    "    # Holds the user's desired primitive-based object template handle\n",
    "    global sel_prim_obj_handle\n",
    "    sel_prim_obj_handle = obj_attr_mgr.get_synth_template_handles()[0]\n",
    "    # Holds the user's desired primitive asset template handle\n",
    "    global sel_asset_handle\n",
    "    sel_asset_handle = prim_attr_mgr.get_template_handles()[0]\n",
    "\n",
    "\n",
    "# [/setup]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "lines_to_next_cell": 2
   },
   "outputs": [],
   "source": [
    "# @title Define Template Dictionary Utility Functions { display-mode: \"form\" }\n",
    "# @markdown (double click to show code)\n",
    "\n",
    "# @markdown This cell defines utility functions that expose Attribute template object properties.\n",
    "\n",
    "\n",
    "# This method builds a dictionary of k-v pairs of attribute property names and\n",
    "# values shared by all attribute template types.  The values are tuples with the\n",
    "# first entry being the value and the second being whether the property is\n",
    "# editable and the third being the type.\n",
    "def build_dict_of_Default_attrs(template):\n",
    "    res_dict = {\n",
    "        \"handle\": (template.handle, True, \"string\"),\n",
    "        # Read-only values\n",
    "        \"template_id\": (template.template_id, False, \"int\"),\n",
    "        \"template_class\": (template.template_class, False, \"string\"),\n",
    "        \"file_directory\": (template.file_directory, False, \"string\"),\n",
    "        \"num_user_configs\": (template.num_user_configs, False, \"int\"),\n",
    "    }\n",
    "    return res_dict\n",
    "\n",
    "\n",
    "# This method builds a dictionary of k-v pairs of attribute property names and\n",
    "# values shared by templates of physically modeled constructs (scenes and\n",
    "# objects). The values are tuples with the first entry being the value and the\n",
    "# second being whether the property is editable and the third being the type.\n",
    "def build_dict_of_PhyObj_attrs(phys_obj_template):\n",
    "    res_dict = build_dict_of_Default_attrs(phys_obj_template)\n",
    "    res_dict[\"scale\"] = (phys_obj_template.scale, True, \"vector\")\n",
    "    res_dict[\"margin\"] = (phys_obj_template.margin, True, \"double\")\n",
    "    res_dict[\"friction_coefficient\"] = (\n",
    "        phys_obj_template.friction_coefficient,\n",
    "        True,\n",
    "        \"double\",\n",
    "    )\n",
    "    res_dict[\"restitution_coefficient\"] = (\n",
    "        phys_obj_template.restitution_coefficient,\n",
    "        True,\n",
    "        \"double\",\n",
    "    )\n",
    "    res_dict[\"render_asset_handle\"] = (\n",
    "        phys_obj_template.render_asset_handle,\n",
    "        True,\n",
    "        \"string\",\n",
    "    )\n",
    "    res_dict[\"collision_asset_handle\"] = (\n",
    "        phys_obj_template.collision_asset_handle,\n",
    "        True,\n",
    "        \"string\",\n",
    "    )\n",
    "    res_dict[\"force_flat_shading\"] = (\n",
    "        phys_obj_template.force_flat_shading,\n",
    "        True,\n",
    "        \"boolean\",\n",
    "    )\n",
    "    res_dict[\"shader_type\"] = (phys_obj_template.shader_type, True, \"int\")\n",
    "    res_dict[\"up\"] = (phys_obj_template.orient_up, True, \"vector\")\n",
    "    res_dict[\"front\"] = (phys_obj_template.orient_front, True, \"vector\")\n",
    "    res_dict[\"units_to_meters\"] = (phys_obj_template.units_to_meters, True, \"double\")\n",
    "    res_dict[\"render_asset_type\"] = (phys_obj_template.render_asset_type, True, \"int\")\n",
    "    res_dict[\"collision_asset_type\"] = (\n",
    "        phys_obj_template.collision_asset_type,\n",
    "        True,\n",
    "        \"int\",\n",
    "    )\n",
    "    # Read-only values\n",
    "    res_dict[\"render_asset_is_primitive\"] = (\n",
    "        phys_obj_template.render_asset_is_primitive,\n",
    "        False,\n",
    "        \"boolean\",\n",
    "    )\n",
    "    res_dict[\"collision_asset_is_primitive\"] = (\n",
    "        phys_obj_template.collision_asset_is_primitive,\n",
    "        False,\n",
    "        \"boolean\",\n",
    "    )\n",
    "    res_dict[\"use_mesh_for_collision\"] = (\n",
    "        phys_obj_template.use_mesh_for_collision,\n",
    "        False,\n",
    "        \"boolean\",\n",
    "    )\n",
    "    res_dict[\"is_collidable\"] = (phys_obj_template.is_collidable, True, \"boolean\")\n",
    "    res_dict[\"filenames_are_dirty\"] = (\n",
    "        phys_obj_template.filenames_are_dirty,\n",
    "        False,\n",
    "        \"boolean\",\n",
    "    )\n",
    "    return res_dict\n",
    "\n",
    "\n",
    "# This method will build a dict containing k-v pairs of attribute property names\n",
    "# and values for the passed object template. The values are tuples with the first\n",
    "# entry being the value,the second being whether the property is editable and\n",
    "# the third being the type.\n",
    "def build_dict_of_Object_attrs(obj_template):\n",
    "    res_dict = build_dict_of_PhyObj_attrs(obj_template)\n",
    "    res_dict[\"com\"] = (obj_template.com, True, \"vector\")\n",
    "    res_dict[\"compute_COM_from_shape\"] = (\n",
    "        obj_template.compute_COM_from_shape,\n",
    "        True,\n",
    "        \"boolean\",\n",
    "    )\n",
    "    res_dict[\"mass\"] = (obj_template.mass, True, \"double\")\n",
    "    res_dict[\"inertia\"] = (obj_template.inertia, True, \"vector\")\n",
    "    res_dict[\"linear_damping\"] = (obj_template.linear_damping, True, \"double\")\n",
    "    res_dict[\"angular_damping\"] = (obj_template.angular_damping, True, \"double\")\n",
    "    res_dict[\"bounding_box_collisions\"] = (\n",
    "        obj_template.bounding_box_collisions,\n",
    "        True,\n",
    "        \"boolean\",\n",
    "    )\n",
    "    res_dict[\"join_collision_meshes\"] = (\n",
    "        obj_template.join_collision_meshes,\n",
    "        True,\n",
    "        \"boolean\",\n",
    "    )\n",
    "    res_dict[\"is_visible\"] = (obj_template.is_visible, True, \"boolean\")\n",
    "    res_dict[\"semantic_id\"] = (obj_template.semantic_id, True, \"int\")\n",
    "    return res_dict\n",
    "\n",
    "\n",
    "# This method will build a dict containing k-v pairs of attribute property names\n",
    "# and values for the passed scene template. The values are tuples with the first\n",
    "# entry being the value,the second being whether the property is editable and\n",
    "# the third being the type.\n",
    "def build_dict_of_Stage_attrs(scene_template):\n",
    "    res_dict = build_dict_of_PhyObj_attrs(scene_template)\n",
    "    res_dict[\"gravity\"] = (scene_template.gravity, True, \"vector\")\n",
    "    res_dict[\"origin\"] = (scene_template.origin, True, \"vector\")\n",
    "    res_dict[\"semantic_asset_handle\"] = (\n",
    "        scene_template.semantic_asset_handle,\n",
    "        True,\n",
    "        \"string\",\n",
    "    )\n",
    "    res_dict[\"semantic_asset_type\"] = (scene_template.semantic_asset_type, True, \"int\")\n",
    "    res_dict[\"navmesh_asset_handle\"] = (\n",
    "        scene_template.navmesh_asset_handle,\n",
    "        True,\n",
    "        \"string\",\n",
    "    )\n",
    "    res_dict[\"house_filename\"] = (scene_template.house_filename, True, \"string\")\n",
    "    res_dict[\"frustum_culling\"] = (scene_template.frustum_culling, True, \"boolean\")\n",
    "    return res_dict\n",
    "\n",
    "\n",
    "# This method will build a dict containing k-v pairs of attribute property names\n",
    "# and values for the passed physics manager template. The values are tuples with\n",
    "# the first entry being the value,the second being whether the property is\n",
    "# editable and the third being the type.\n",
    "def build_dict_of_PhysicsSim_attrs(physics_template):\n",
    "    res_dict = build_dict_of_Default_attrs(physics_template)\n",
    "    res_dict[\"gravity\"] = (physics_template.gravity, True, \"vector\")\n",
    "    res_dict[\"timestep\"] = (physics_template.timestep, True, \"double\")\n",
    "    res_dict[\"max_substeps\"] = (physics_template.max_substeps, True, \"int\")\n",
    "    res_dict[\"friction_coefficient\"] = (\n",
    "        physics_template.friction_coefficient,\n",
    "        True,\n",
    "        \"double\",\n",
    "    )\n",
    "    res_dict[\"restitution_coefficient\"] = (\n",
    "        physics_template.restitution_coefficient,\n",
    "        True,\n",
    "        \"double\",\n",
    "    )\n",
    "    # Read-only values\n",
    "    res_dict[\"simulator\"] = (physics_template.simulator, False, \"string\")\n",
    "    return res_dict\n",
    "\n",
    "\n",
    "# This method will build a dict containing k-v pairs of attribute property names\n",
    "# and values that are shared among all primitive asset attributes templates.\n",
    "# The values are tuples with the first entry being the value,the second being\n",
    "# whether the property is editable and the third being the type.\n",
    "def build_dict_of_prim_attrs(prim_template):\n",
    "    res_dict = build_dict_of_Default_attrs(prim_template)\n",
    "    res_dict[\"use_texture_coords\"] = (prim_template.use_texture_coords, True, \"boolean\")\n",
    "    res_dict[\"use_tangents\"] = (prim_template.use_tangents, True, \"boolean\")\n",
    "    res_dict[\"num_rings\"] = (prim_template.num_rings, True, \"int\")\n",
    "    res_dict[\"num_segments\"] = (prim_template.num_segments, True, \"int\")\n",
    "    res_dict[\"half_length\"] = (prim_template.half_length, True)\n",
    "    # Read-only values\n",
    "    res_dict[\"prim_obj_class_name\"] = (\n",
    "        prim_template.prim_obj_class_name,\n",
    "        False,\n",
    "        \"string\",\n",
    "    )\n",
    "    res_dict[\"prim_obj_type\"] = (prim_template.prim_obj_type, False, \"int\")\n",
    "    res_dict[\"is_valid_template\"] = (prim_template.is_valid_template, False, \"boolean\")\n",
    "    return res_dict\n",
    "\n",
    "\n",
    "# This method will build a dict containing k-v pairs of attribute property names\n",
    "# and values for the passed capsule primitive template. The values are tuples with\n",
    "# the first entry being the value,the second being whether the property is\n",
    "# editable and the third being the type.\n",
    "def build_dict_of_Capsule_prim_attrs(capsule_template):\n",
    "    res_dict = build_dict_of_prim_attrs(capsule_template)\n",
    "    res_dict[\"hemisphere_rings\"] = (capsule_template.hemisphere_rings, True, \"int\")\n",
    "    res_dict[\"cylinder_rings\"] = (capsule_template.cylinder_rings, True, \"int\")\n",
    "    return res_dict\n",
    "\n",
    "\n",
    "# This method will build a dict containing k-v pairs of attribute property names\n",
    "# and values for the passed cone primitive template. The values are tuples with\n",
    "# the first entry being the value,the second being whether the property is\n",
    "# editable and the third being the type.\n",
    "def build_dict_of_Cone_prim_attrs(cone_template):\n",
    "    res_dict = build_dict_of_prim_attrs(cone_template)\n",
    "    res_dict[\"use_cap_end\"] = (cone_template.use_cap_end, True, \"boolean\")\n",
    "    return res_dict\n",
    "\n",
    "\n",
    "# This method will build a dict containing k-v pairs of attribute property names\n",
    "# and values for the passed cube primitive template. The values are tuples with\n",
    "# the first entry being the value,the second being whether the property is\n",
    "# editable and the third being the type.\n",
    "def build_dict_of_Cube_prim_attrs(cube_template):\n",
    "    res_dict = build_dict_of_prim_attrs(cube_template)\n",
    "    return res_dict\n",
    "\n",
    "\n",
    "# This method will build a dict containing k-v pairs of attribute property names\n",
    "# and values for the passed cylinder primitive template. The values are tuples with\n",
    "# the first entry being the value,the second being whether the property is\n",
    "# editable and the third being the type.\n",
    "def build_dict_of_Cylinder_prim_attrs(cylinder_template):\n",
    "    res_dict = build_dict_of_prim_attrs(cylinder_template)\n",
    "    res_dict[\"use_cap_ends\"] = (cylinder_template.use_cap_ends, True, \"boolean\")\n",
    "    return res_dict\n",
    "\n",
    "\n",
    "# This method will build a dict containing k-v pairs of attribute property names\n",
    "# and values for the passed icosphere primitive template. The values are tuples with\n",
    "# the first entry being the value,the second being whether the property is\n",
    "# editable and the third being the type.\n",
    "def build_dict_of_Icosphere_prim_attrs(icosphere_template):\n",
    "    res_dict = build_dict_of_prim_attrs(icosphere_template)\n",
    "    res_dict[\"subdivisions\"] = (icosphere_template.subdivisions, True, \"int\")\n",
    "    return res_dict\n",
    "\n",
    "\n",
    "# This method will build a dict containing k-v pairs of attribute property names\n",
    "# and values for the passed UV-Sphere primitive template. The values are tuples with\n",
    "# the first entry being the value,the second being whether the property is\n",
    "# editable and the third being the type.\n",
    "def build_dict_of_UVSphere_prim_attrs(uvsphere_template):\n",
    "    res_dict = build_dict_of_prim_attrs(uvsphere_template)\n",
    "    return res_dict\n",
    "\n",
    "\n",
    "# This method will deduce the appropriate template type and build the subsequent\n",
    "# dictionary containing containing k-v pairs of template property names\n",
    "# and values for the passed template. The values are tuples with\n",
    "# the first entry being the value,the second being whether the property is\n",
    "# editable and the third being the type.\n",
    "def build_dict_from_template(template):\n",
    "    template_class = template.template_class\n",
    "    if \"ObjectAttributes\" in template_class:\n",
    "        return build_dict_of_Object_attrs(template)\n",
    "    if \"StageAttributes\" in template_class:\n",
    "        return build_dict_of_Stage_attrs(template)\n",
    "    if \"PhysicsManagerAttributes\" in template_class:\n",
    "        return build_dict_of_PhysicsSim_attrs(template)\n",
    "    if \"CapsulePrimitiveAttributes\" in template_class:\n",
    "        return build_dict_of_Capsule_prim_attrs(template)\n",
    "    if \"ConePrimitiveAttributes\" in template_class:\n",
    "        return build_dict_of_Cone_prim_attrs(template)\n",
    "    if \"CubePrimitiveAttributes\" in template_class:\n",
    "        return build_dict_of_Cube_prim_attrs(template)\n",
    "    if \"CylinderPrimitiveAttributes\" in template_class:\n",
    "        return build_dict_of_Cylinder_prim_attrs(template)\n",
    "    if \"IcospherePrimitiveAttributes\" in template_class:\n",
    "        return build_dict_of_Icosphere_prim_attrs(template)\n",
    "    if \"UVSpherePrimitiveAttributes\" in template_class:\n",
    "        return build_dict_of_UVSphere_prim_attrs(template)\n",
    "    print(\"Unknown template type : %s \" % template_class)\n",
    "    return None\n",
    "\n",
    "\n",
    "# This will set a template's attributes from the passed dictionary\n",
    "def set_template_properties_from_dict(template, template_dict):\n",
    "    for k, v in template_dict.items():\n",
    "        setattr(template, k, v[0])\n",
    "    return template\n",
    "\n",
    "\n",
    "# This will display all the properties of an attributes template\n",
    "def show_template_properties(template):\n",
    "    template_dict = build_dict_from_template(template)\n",
    "    print(\"Template {} has : \".format(template.handle))\n",
    "    for k, v in template_dict.items():\n",
    "        print(\n",
    "            \"\\tProperty {} has value {} of type {} that is editable : {}\".format(\n",
    "                k, v[0], v[2], v[1]\n",
    "            )\n",
    "        )\n",
    "\n",
    "\n",
    "# @title Define Simulation Utility Functions { display-mode: \"form\" }\n",
    "# @markdown (double click to show code)\n",
    "\n",
    "\n",
    "# @markdown - simulate\n",
    "def simulate(sim, dt=1.0, get_frames=True):\n",
    "    # simulate dt seconds at 60Hz to the nearest fixed timestep\n",
    "    print(\"Simulating {:.3f} world seconds.\".format(dt))\n",
    "    observations = []\n",
    "    start_time = sim.get_world_time()\n",
    "    while sim.get_world_time() < start_time + dt:\n",
    "        sim.step_physics(1.0 / 60.0)\n",
    "        if get_frames:\n",
    "            observations.append(sim.get_sensor_observations())\n",
    "    return observations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# @title Define GUI Utility Functions { display-mode: \"form\" }\n",
    "# @markdown (double click to show code)\n",
    "\n",
    "# @markdown This cell provides utility functions to build and manage IPyWidget interactive components.\n",
    "\n",
    "\n",
    "# Event handler for dropdowns displaying file-based object handles\n",
    "def on_file_obj_ddl_change(ddl_values):\n",
    "    global sel_file_obj_handle\n",
    "    sel_file_obj_handle = ddl_values[\"new\"]\n",
    "    return sel_file_obj_handle\n",
    "\n",
    "\n",
    "# Event handler for dropdowns displaying prim-based object handles\n",
    "def on_prim_obj_ddl_change(ddl_values):\n",
    "    global sel_prim_obj_handle\n",
    "    sel_prim_obj_handle = ddl_values[\"new\"]\n",
    "    return sel_prim_obj_handle\n",
    "\n",
    "\n",
    "# Event handler for dropdowns displaying asset handles\n",
    "def on_prim_ddl_change(ddl_values):\n",
    "    global sel_asset_handle\n",
    "    sel_asset_handle = ddl_values[\"new\"]\n",
    "    return sel_asset_handle\n",
    "\n",
    "\n",
    "# Build a dropdown list holding obj_handles and set its event handler\n",
    "def set_handle_ddl_widget(obj_handles, handle_types, sel_handle, on_change):\n",
    "    sel_handle = obj_handles[0]\n",
    "    descStr = handle_types + \" Template Handles:\"\n",
    "    style = {\"description_width\": \"300px\"}\n",
    "    obj_ddl = widgets.Dropdown(\n",
    "        options=obj_handles,\n",
    "        value=sel_handle,\n",
    "        description=descStr,\n",
    "        style=style,\n",
    "        disabled=False,\n",
    "        layout={\"width\": \"max-content\"},\n",
    "    )\n",
    "\n",
    "    obj_ddl.observe(on_change, names=\"value\")\n",
    "    return obj_ddl, sel_handle\n",
    "\n",
    "\n",
    "def set_button_launcher(desc):\n",
    "    button = widgets.Button(\n",
    "        description=desc,\n",
    "        layout={\"width\": \"max-content\"},\n",
    "    )\n",
    "    return button\n",
    "\n",
    "\n",
    "def make_sim_and_vid_button(prefix, dt=1.0):\n",
    "    if not HAS_WIDGETS:\n",
    "        return\n",
    "\n",
    "    def on_sim_click(b):\n",
    "        observations = simulate(sim, dt=dt)\n",
    "        vut.make_video(\n",
    "            observations,\n",
    "            \"color_sensor_1st_person\",\n",
    "            \"color\",\n",
    "            output_path + prefix,\n",
    "            open_vid=show_video,\n",
    "        )\n",
    "\n",
    "    sim_and_vid_btn = set_button_launcher(\"Simulate and Make Video\")\n",
    "    sim_and_vid_btn.on_click(on_sim_click)\n",
    "    ipydisplay(sim_and_vid_btn)\n",
    "\n",
    "\n",
    "def make_clear_all_objects_button():\n",
    "    if not HAS_WIDGETS:\n",
    "        return\n",
    "\n",
    "    def on_clear_click(b):\n",
    "        rigid_obj_mgr.remove_all_objects()\n",
    "\n",
    "    clear_objs_button = set_button_launcher(\"Clear all objects\")\n",
    "    clear_objs_button.on_click(on_clear_click)\n",
    "    ipydisplay(clear_objs_button)\n",
    "\n",
    "\n",
    "# Builds widget-based UI components\n",
    "def build_widget_ui(obj_attr_mgr, prim_attr_mgr):\n",
    "    # Holds the user's desired file-based object template handle\n",
    "    global sel_file_obj_handle\n",
    "    sel_file_obj_handle = \"\"\n",
    "\n",
    "    # Holds the user's desired primitive-based object template handle\n",
    "    global sel_prim_obj_handle\n",
    "    sel_prim_obj_handle = \"\"\n",
    "\n",
    "    # Holds the user's desired primitive asset template handle\n",
    "    global sel_asset_handle\n",
    "    sel_asset_handle = \"\"\n",
    "\n",
    "    # Construct DDLs and assign event handlers\n",
    "    # All file-based object template handles\n",
    "    file_obj_handles = obj_attr_mgr.get_file_template_handles()\n",
    "    # All primitive asset-based object template handles\n",
    "    prim_obj_handles = obj_attr_mgr.get_synth_template_handles()\n",
    "    # All primitive asset handles template handles\n",
    "    prim_asset_handles = prim_attr_mgr.get_template_handles()\n",
    "    # If not using widgets, set as first available handle\n",
    "    if not HAS_WIDGETS:\n",
    "        sel_file_obj_handle = file_obj_handles[0]\n",
    "        sel_prim_obj_handle = prim_obj_handles[0]\n",
    "        sel_prim_obj_handle = prim_asset_handles[0]\n",
    "        return\n",
    "\n",
    "    # Build widgets\n",
    "    file_obj_ddl, sel_file_obj_handle = set_handle_ddl_widget(\n",
    "        file_obj_handles,\n",
    "        \"File-based Object\",\n",
    "        sel_file_obj_handle,\n",
    "        on_file_obj_ddl_change,\n",
    "    )\n",
    "    prim_obj_ddl, sel_prim_obj_handle = set_handle_ddl_widget(\n",
    "        prim_obj_handles,\n",
    "        \"Primitive-based Object\",\n",
    "        sel_prim_obj_handle,\n",
    "        on_prim_obj_ddl_change,\n",
    "    )\n",
    "    prim_asset_ddl, sel_asset_handle = set_handle_ddl_widget(\n",
    "        prim_asset_handles, \"Primitive Asset\", sel_asset_handle, on_prim_ddl_change\n",
    "    )\n",
    "    # Display DDLs\n",
    "    ipydisplay(file_obj_ddl)\n",
    "    ipydisplay(prim_obj_ddl)\n",
    "    ipydisplay(prim_asset_ddl)\n",
    "\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    import argparse\n",
    "\n",
    "    parser = argparse.ArgumentParser()\n",
    "    parser.add_argument(\"--no-display\", dest=\"display\", action=\"store_false\")\n",
    "    parser.add_argument(\"--no-make-video\", dest=\"make_video\", action=\"store_false\")\n",
    "    parser.set_defaults(show_video=True, make_video=True)\n",
    "    args, _ = parser.parse_known_args()\n",
    "    show_video = args.display\n",
    "    display = args.display\n",
    "    make_video = args.make_video\n",
    "else:\n",
    "    show_video = False\n",
    "    make_video = False\n",
    "    display = False"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# View Assets in Habitat-sim\n",
    "Use the code in this section to view assets in the Habitat-sim engine."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# [initialize]\n",
    "# @title Initialize Simulator and Load Objects { display-mode: \"form\" }\n",
    "\n",
    "sim_settings = make_default_settings()\n",
    "sim_settings[\"scene\"] = \"none\"\n",
    "sim_settings[\"sensor_pitch\"] = 0.0\n",
    "sim_settings[\"override_scene_light_defaults\"] = True\n",
    "sim_settings[\"scene_light_setup\"] = \"\"\n",
    "\n",
    "# use 3rd person camera\n",
    "sim_settings[\"color_sensor_3rd_person\"] = True\n",
    "\n",
    "make_simulator_from_settings(sim_settings)\n",
    "# [/initialize]\n",
    "\n",
    "# [specify_object]\n",
    "# @markdown Drag a stage or object asset into a directory at the left,\n",
    "# @markdown and then load it below by setting object_to_view_path\n",
    "# @markdown Put the full path to the asset you would like to view here :\n",
    "# fmt: off\n",
    "object_to_view_path = os.path.join(data_path, \"test_assets/scenes/simple_room.glb\")  # @param {type:\"string\"}\n",
    "# fmt: on\n",
    "\n",
    "# this is the name to save the resultant video with\n",
    "clip_short_name = object_to_view_path.split(\"/\")[-1].split(\".\")[0]\n",
    "\n",
    "# [/specify_object]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Synthesize Carousel View\n",
    "This cell will make a carousel view of the loaded stage."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# [build_carousel_view]\n",
    "\n",
    "# @markdown This cell loads the object, centers it, and then moves a camera in a circle around\n",
    "# @markdown the center of the scene, recording observations, which are subsequently stitched\n",
    "# @markdown together to build a video of the object\n",
    "\n",
    "# check if desired object actually exists\n",
    "if os.path.exists(object_to_view_path) and os.path.isfile(object_to_view_path):\n",
    "    # Acquire the sensor being used\n",
    "    visual_sensor = sim._sensors[\"color_sensor_3rd_person\"]\n",
    "    initial_sensor_position = np.array(visual_sensor._spec.position)\n",
    "    initial_sensor_orientation = np.array(visual_sensor._spec.orientation)\n",
    "\n",
    "    # load an object template and instantiate an object to view\n",
    "    object_template = obj_attr_mgr.create_new_template(str(object_to_view_path), False)\n",
    "    # if using a stage and it displays sideways, you may need to reorient it via its attributes for it to display properly.\n",
    "\n",
    "    # @markdown If the asset being displayed is on its side, enable orientation_correction below :\n",
    "    orientation_correction = False  # @param {type: \"boolean\"}\n",
    "    # This will correct the orientation (Dependent on PR : )\n",
    "    if orientation_correction:\n",
    "        object_template.orient_up = (0.0, 0.0, 1.0)\n",
    "        object_template.orient_front = (0.0, 1.0, 0.0)\n",
    "\n",
    "    # modify template here if desired and then register it\n",
    "    obj_temp_id = obj_attr_mgr.register_template(object_template)\n",
    "\n",
    "    # create object\n",
    "    obj = rigid_obj_mgr.add_object_by_template_id(obj_temp_id)\n",
    "    # place object in center - must be done before setting to static\n",
    "    # get bb of object\n",
    "    obj_bbox = obj.aabb\n",
    "    # find center of bb and move to scene origin - this centers object\n",
    "    obj.translation = -obj_bbox.center()\n",
    "    # get max dim to use as scale for sensor placement\n",
    "    bb_scale = max(obj_bbox.max)\n",
    "    # determine sensor placement based on size of object\n",
    "    sensor_pos = bb_scale * mn.Vector3(0.0, 1.0, 2.0)\n",
    "    # set object to be static\n",
    "    obj.motion_type = habitat_sim.physics.MotionType.STATIC\n",
    "\n",
    "    # initialize an agent and set its initial state\n",
    "    agent = sim.initialize_agent(sim_settings[\"default_agent\"])\n",
    "    agent_state = habitat_sim.AgentState()\n",
    "    agent_state.position = mn.Vector3(0.0, 0.0, 0.0)  # in world space\n",
    "    agent.set_state(agent_state)\n",
    "\n",
    "    # set the sensor to be behind and above the agent's initial loc\n",
    "    # distance is scaled by size of largest object dimension\n",
    "    visual_sensor._spec.position = agent_state.position + sensor_pos\n",
    "    visual_sensor._spec.orientation = mn.Vector3(-0.5, 0.0, 0.0)\n",
    "    visual_sensor._sensor_object.set_transformation_from_spec()\n",
    "\n",
    "    # Create observations array\n",
    "    observations = []\n",
    "\n",
    "    # @markdown Set how long the resultant video should be, in seconds.  The object will make 1 full revolution during this time.\n",
    "    video_length = 4.8  # @param {type:\"slider\", min:1.0, max:20.0, step:0.1}\n",
    "    # Sim time step\n",
    "    time_step = 1.0 / 60.0\n",
    "    # Amount to rotate per frame to make 1 full rotation\n",
    "    rot_amount = 2 * math.pi / (video_length / time_step)\n",
    "\n",
    "    # simulate with updated camera at each frame\n",
    "    start_time = sim.get_world_time()\n",
    "    while sim.get_world_time() - start_time < video_length:\n",
    "        sim.step_physics(time_step)\n",
    "        # rotate the agent to rotate the camera\n",
    "        agent_state.rotation *= ut.quat_from_angle_axis(\n",
    "            rot_amount, np.array([0.0, 1.0, 0.0])\n",
    "        )\n",
    "        agent.set_state(agent_state)\n",
    "\n",
    "        observations.append(sim.get_sensor_observations())\n",
    "\n",
    "    # video rendering of carousel view\n",
    "    video_prefix = clip_short_name + \"_scene_view\"\n",
    "    if make_video:\n",
    "        vut.make_video(\n",
    "            observations,\n",
    "            \"color_sensor_3rd_person\",\n",
    "            \"color\",\n",
    "            output_path + video_prefix,\n",
    "            open_vid=show_video,\n",
    "            video_dims=[1280, 720],\n",
    "        )\n",
    "\n",
    "    # reset the sensor state for other examples\n",
    "    visual_sensor._spec.position = initial_sensor_position\n",
    "    visual_sensor._spec.orientation = initial_sensor_orientation\n",
    "    visual_sensor._sensor_object.set_transformation_from_spec()\n",
    "\n",
    "    # remove added objects\n",
    "    rigid_obj_mgr.remove_all_objects()\n",
    "else:\n",
    "    print(\n",
    "        \"\\nChosen File : '{}' does not exist or cannot be found. Aborting.\\n\".format(\n",
    "            object_to_view_path\n",
    "        )\n",
    "    )\n",
    "# [/build_carousel_view]"
   ]
  }
 ],
 "metadata": {
  "accelerator": "GPU",
  "jupytext": {
   "cell_metadata_filter": "-all",
   "formats": "nb_python//py:percent,notebooks//ipynb",
   "notebook_metadata_filter": "all"
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.17"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
